package com.frank.oj.manager;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.frank.model.dto.TestJudgeReq;
import com.frank.model.dto.TestJudgeRes;
import com.frank.model.entity.contest.Contest;
import com.frank.model.entity.contest.ContestRecord;
import com.frank.model.entity.judge.Judge;
import com.frank.model.entity.judge.JudgeCase;
import com.frank.model.entity.problem.Problem;
import com.frank.model.entity.user.UserAcproblem;
import com.frank.oj.annotation.HCOJAccessEnum;
import com.frank.oj.common.exception.*;
import com.frank.oj.config.NacosSwitchConfig;
import com.frank.oj.config.SwitchConfig;
import com.frank.oj.constant.Constants;
import com.frank.oj.core.dispatcher.JudgeDispatcher;
import com.frank.oj.core.dispatcher.RemoteJudgeDispatcher;
import com.frank.oj.exception.AccessException;
import com.frank.oj.model.dto.SubmitIdListDTO;
import com.frank.oj.model.dto.SubmitJudgeDTO;
import com.frank.oj.model.dto.TestJudgeDTO;
import com.frank.oj.model.vo.*;
import com.frank.oj.service.entity.contest.ContestEntityService;
import com.frank.oj.service.entity.contest.ContestRecordEntityService;
import com.frank.oj.service.entity.judge.JudgeCaseEntityService;
import com.frank.oj.service.entity.judge.JudgeEntityService;
import com.frank.oj.service.entity.problem.ProblemEntityService;
import com.frank.oj.service.entity.user.UserAcProblemEntityService;
import com.frank.oj.shiro.AccountProfile;
import com.frank.oj.utils.IpUtils;
import com.frank.oj.utils.RedisUtils;
import com.frank.oj.validator.AccessValidator;
import com.frank.oj.validator.ContestValidator;
import com.frank.oj.validator.GroupValidator;
import com.frank.oj.validator.JudgeValidator;
import org.apache.shiro.SecurityUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author frank
 * @Date 2024/3/24
 */
@Component
public class JudgeManager {
    @Resource
    private JudgeEntityService judgeEntityService;
    @Resource
    private JudgeCaseEntityService judgeCaseEntityService;

    @Resource
    private ProblemEntityService problemEntityService;

    @Resource
    private ContestEntityService contestEntityService;

    @Resource
    private ContestRecordEntityService contestRecordEntityService;

    @Resource
    private UserAcProblemEntityService userAcproblemEntityService;

    @Resource
    private JudgeDispatcher judgeDispatcher;

    @Resource
    private RemoteJudgeDispatcher remoteJudgeDispatcher;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private JudgeValidator judgeValidator;

    @Resource
    private ContestValidator contestValidator;

    @Resource
    private BeforeDispatchInitManager beforeDispatchInitManager;

    @Resource
    private GroupValidator groupValidator;

    @Resource
    private AccessValidator accessValidator;

    @Resource
    private NacosSwitchConfig nacosSwitchConfig;

    public IPage<JudgeVO> getJudgeList(Integer limit,
                                       Integer currentPage,
                                       Boolean onlyMine,
                                       String searchPid,
                                       Integer searchStatus,
                                       String searchUsername,
                                       Boolean completeProblemID,
                                       Long gid)
            throws StatusAccessDeniedException {

        if (currentPage == null || currentPage < 1) currentPage = 1;
        if (limit == null || limit < 1) limit = 10;
        String uid = null;

        if (onlyMine) {
            AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();

            if (userRole == null) {
                throw new StatusAccessDeniedException("当前未登录，请登陆后重试！");
            }
            uid = userRole.getUid();
        }
        if (searchPid != null) {
            searchPid = searchPid.trim();
        }
        if (searchUsername != null) {
            searchUsername = searchUsername.trim();
        }

        return judgeEntityService.getCommonJudgeList(limit, currentPage, uid, searchPid, searchStatus, searchUsername, completeProblemID, gid);
    }

    public SubmissionInfoVO getSubmission(Long submitId) throws StatusNotFoundException, StatusAccessDeniedException {
        Judge judge = judgeEntityService.getById(submitId);
        if (judge == null) {
            throw new StatusNotFoundException("提交记录不存在！");
        }

        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        boolean isRoot = SecurityUtils.getSubject().hasRole("root");

        // 清空vj（远程判题）信息
        judge.setVjudgeUsername(null);
        judge.setVjudgeSubmitId(null);
        judge.setVjudgePassword(null);

        SubmissionInfoVO submissionInfoVO = new SubmissionInfoVO();

        if (judge.getCid() != 0) { //比赛
            if (userRole == null) {
                throw new StatusAccessDeniedException("请登录后重试");
            }

            Contest contest = contestEntityService.getById(judge.getCid());
            if (!isRoot && !userRole.getUid().equals(contest.getUid()) &&
                    !(judge.getGid() != null && groupValidator.isGroupRoot(userRole.getUid(), judge.getGid()))) { //普通用户
                // 如果是比赛,那么还需要判断是否为封榜,比赛管理员和超级管理员可以有权限查看(ACM题目除外)
                if (contest.getType().intValue() == Constants.Contest.TYPE_OI.getCode()
                        && contestValidator.isSealRank(userRole.getUid(), contest, true, false)) {
                    submissionInfoVO.setSubmission(new Judge().setStatus(Constants.Judge.STATUS_SUBMITTED_UNKNOWN_RESULT.getStatus()));
                    return submissionInfoVO;
                }

                //    非本人
                if (!userRole.getUid().equals(judge.getUid())) {
                    //不能查看提交代码
                    judge.setCode(null);
                    // 比赛进行中，，不能查看时间、空间、错误提示等等
                    if (contest.getStatus().intValue() == Constants.Contest.STATUS_RUNNING.getCode()) {
                        judge.setTime(null);
                        judge.setMemory(null);
                        judge.setLength(null);
                        judge.setErrorMessage("The contest is in progress. You are not allowed to view other people's error information.");
                    }
                }
            }
            // 团队比赛的提交代码 如果不是超管，需要检查网站是否开启隐藏代码功能
            if (!isRoot && contest.getIsGroup() && judge.getCode() != null) {
                try {
                    accessValidator.validateAccess(HCOJAccessEnum.HIDE_NON_CONTEST_SUBMISSION_CODE);
                } catch (AccessException e) {
                    judge.setCode("Because the super administrator has enabled " +
                            "the function of not viewing the submitted code outside the contest of master station, \n" +
                            "the code of this submission details has been hidden.");
                }
            }
        } else { //非比赛提交
            boolean isProblemAdmin = SecurityUtils.getSubject().hasRole("problem_admin");// 是否为题目管理员
            if (!judge.getShare() //未共享
                    && !isRoot
                    && !isProblemAdmin
                    && !(judge.getGid() != null && groupValidator.isGroupRoot(userRole.getUid(), judge.getGid()))) {
                if (userRole != null) {
                    if (!judge.getUid().equals(userRole.getUid())) { //本人
                        judge.setCode(null);
                    }
                } else {
                    judge.setCode(null);
                }
            }
            // 比赛外的提交代码 如果不是超管或题目管理员，需要检查网站是否开启隐藏代码功能
            if (!isRoot && !isProblemAdmin && judge.getCode() != null) {
                try {
                    accessValidator.validateAccess(HCOJAccessEnum.HIDE_NON_CONTEST_SUBMISSION_CODE);
                } catch (AccessException e) {
                    judge.setCode("Because the super administrator has enabled " +
                            "the function of not viewing the submitted code outside the contest of master station, \n" +
                            "the code of this submission details has been hidden.");
                }
            }
        }
        Problem problem = problemEntityService.getById(judge.getPid());
        // todo：错误展示设置
        //if (judge.getStatus().intValue() != Constants.Judge.STATUS_COMPILE_ERROR.getStatus() &&
        //        judge.getStatus().intValue() != Constants.Judge.STATUS_SYSTEM_ERROR.getStatus() &&
        //        judge.getStatus().intValue() != Constants.Judge.STATUS_SUBMITTED_FAILED.getStatus()) {
        //    judge.setErrorMessage("The error message does not support viewing.");
        //}
        submissionInfoVO.setSubmission(judge);
        submissionInfoVO.setCodeShare(problem.getCodeShare());

        return submissionInfoVO;
    }

    public JudgeCaseVO getALLCaseResult(Long submitId) throws StatusNotFoundException, StatusForbiddenException {

        Judge judge = judgeEntityService.getById(submitId);
        if (judge == null) {
            throw new StatusNotFoundException("提交记录不存在！");
        }
        Problem problem = problemEntityService.getById(judge.getPid());
        //测试点是否开放
        if (!problem.getOpenCaseResult()) {
            return null;
        }
        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        QueryWrapper<JudgeCase> judgeCaseQueryWrapper = new QueryWrapper<>();
        if (judge.getCid() == 0) { //非比赛
            // 未登录或登录后非管理员仅看这些信息
            if (userRole == null) {
                judgeCaseQueryWrapper.select("time", "memory", "score", "status", "user_output", "group_num", "seq", "mode");
            } else {
                boolean isRoot = SecurityUtils.getSubject().hasRole("root");
                if (!isRoot && !SecurityUtils.getSubject().hasRole("admin") && !SecurityUtils.getSubject().hasRole("problem_admin")) {
                    judgeCaseQueryWrapper.select("time", "memory", "score", "status", "user_output", "group_num", "seq", "mode");
                }
            }
        } else { //比赛
            if (userRole == null) {
                throw new StatusForbiddenException("请先登录！");
            }
            boolean isRoot = SecurityUtils.getSubject().hasRole("root");
            if (!isRoot) {
                Contest contest = contestEntityService.getById(judge.getCid());
                if (!contest.getUid().equals(userRole.getUid()) || (contest.getIsGroup() && !groupValidator.isGroupRoot(userRole.getUid(), contest.getGid()))) {
                    //    ACM比赛期间强制禁止查看,比赛管理员除外（赛后恢复正常）
                    if (contest.getType().intValue() == Constants.Contest.TYPE_ACM.getCode()) {
                        if (contest.getStatus().intValue() == Constants.Contest.STATUS_RUNNING.getCode()) {
                            return null;
                        }
                    } else {
                        if (contest.getSealRank()
                                && contest.getStatus().intValue() == Constants.Contest.STATUS_RUNNING.getCode()
                                && contest.getSealRankTime().before(new Date())) {
                            return null;
                        }
                    }
                    judgeCaseQueryWrapper.select("time", "memory", "score", "status", "user_output", "group_num", "seq", "mode");
                }
            }
        }
        judgeCaseQueryWrapper.eq("submit_id", submitId);
        if (!problem.getIsRemote()) {
            judgeCaseQueryWrapper.last("order by seq asc");
        }

        List<JudgeCase> judgeCaseList = judgeCaseEntityService.list(judgeCaseQueryWrapper);
        JudgeCaseVO judgeCaseVO = new JudgeCaseVO();
        if (!CollectionUtils.isEmpty(judgeCaseList)) {
            String mode = judgeCaseList.get(0).getMode();
            Constants.JudgeCaseMode judgeCaseMode = Constants.JudgeCaseMode.getJudgeCaseMode(mode);
            switch (judgeCaseMode) {
                case DEFAULT:
                case ERGODIC_WITHOUT_ERROR:
                    judgeCaseVO.setJudgeCaseList(judgeCaseList);
                    break;
                case SUBTASK_AVERAGE:
                case SUBTASK_LOWEST:
                    judgeCaseVO.setSubTaskJudgeCaseVoList(buildSubTaskDetail(judgeCaseList, judgeCaseMode));
                    break;
            }
            judgeCaseVO.setJudgeCaseMode(judgeCaseMode.getMode());
        } else {
            judgeCaseVO.setJudgeCaseList(judgeCaseList);
            judgeCaseVO.setJudgeCaseMode(Constants.JudgeCaseMode.DEFAULT.getMode());
        }
        return judgeCaseVO;
    }

    /**
     * 构建子任务详情列表。
     *
     * @param judgeCaseList 判题案例列表，包含各个判题结果的信息。
     * @param judgeCaseMode 判题案例模式，决定如何处理和计算判题结果。
     * @return 返回一个子任务详情的视图对象列表，每个视图对象包含了一个子任务的判题详情。
     */
    private List<SubTaskJudgeCaseVO> buildSubTaskDetail(List<JudgeCase> judgeCaseList, Constants.JudgeCaseMode judgeCaseMode) {
        List<SubTaskJudgeCaseVO> subTaskJudgeCaseVOS = new ArrayList<>();
        // 根据组号和序列号对判题案例进行排序和分组
        LinkedHashMap<Integer, List<JudgeCase>> groupJudgeCaseMap = judgeCaseList.stream()
                .sorted(Comparator.comparing(JudgeCase::getGroupNum).thenComparingInt(JudgeCase::getSeq))
                .collect(Collectors.groupingBy(JudgeCase::getGroupNum, LinkedHashMap::new, Collectors.toList()));

        if (judgeCaseMode == Constants.JudgeCaseMode.SUBTASK_AVERAGE) { // 子任务平均分
            // 处理每个分组，计算平均分和状态
            for (Map.Entry<Integer, List<JudgeCase>> entry : groupJudgeCaseMap.entrySet()) {
                int sumScore = 0; // 总分数
                boolean hasNotACJudgeCase = false; // 是否存在未通过的判题案例
                int acCount = 0; // 通过的判题案例数量
                // 遍历当前分组的所有判题案例
                for (JudgeCase judgeCase : entry.getValue()) {
                    sumScore += judgeCase.getScore();
                    if (!Objects.equals(Constants.Judge.STATUS_ACCEPTED.getStatus(), judgeCase.getStatus())) {
                        hasNotACJudgeCase = true;
                    } else {
                        acCount++;
                    }
                }
                SubTaskJudgeCaseVO subTaskJudgeCaseVo = new SubTaskJudgeCaseVO();
                subTaskJudgeCaseVo.setGroupNum(entry.getKey());
                subTaskJudgeCaseVo.setSubtaskDetailList(entry.getValue());
                subTaskJudgeCaseVo.setAc(acCount);
                subTaskJudgeCaseVo.setTotal(entry.getValue().size());
                int score = (int) Math.round(sumScore * 1.0 / entry.getValue().size());
                subTaskJudgeCaseVo.setScore(score);
                // 根据是否全部通过及平均分数，设置状态
                if (hasNotACJudgeCase) {
                    if (score == 0) {
                        subTaskJudgeCaseVo.setStatus(Constants.Judge.STATUS_WRONG_ANSWER.getStatus());
                    } else {
                        subTaskJudgeCaseVo.setStatus(Constants.Judge.STATUS_PARTIAL_ACCEPTED.getStatus());
                    }
                } else {
                    subTaskJudgeCaseVo.setStatus(Constants.Judge.STATUS_ACCEPTED.getStatus());
                }
                subTaskJudgeCaseVOS.add(subTaskJudgeCaseVo);
            }
        } else { // 子任务最低分
            // 处理每个分组，选择最低分的判题案例作为代表
            for (Map.Entry<Integer, List<JudgeCase>> entry : groupJudgeCaseMap.entrySet()) {
                int minScore = 2147483647; // 最低分数
                JudgeCase finalResJudgeCase = null; // 最终结果的判题案例
                int acCount = 0; // 通过的判题案例数量
                // 遍历当前分组的所有判题案例
                for (JudgeCase judgeCase : entry.getValue()) {
                    if (!Constants.Judge.STATUS_CANCELLED.getStatus().equals(judgeCase.getStatus())) {
                        if (judgeCase.getScore() < minScore) {
                            finalResJudgeCase = judgeCase;
                            minScore = judgeCase.getScore();
                        }
                        if (Objects.equals(Constants.Judge.STATUS_ACCEPTED.getStatus(), judgeCase.getStatus())) {
                            acCount++;
                        }
                    }
                }
                SubTaskJudgeCaseVO subTaskJudgeCaseVo = new SubTaskJudgeCaseVO();
                subTaskJudgeCaseVo.setGroupNum(entry.getKey());
                subTaskJudgeCaseVo.setAc(acCount);
                subTaskJudgeCaseVo.setTotal(entry.getValue().size());
                // 设置最终结果的信息
                if (finalResJudgeCase != null) {
                    subTaskJudgeCaseVo.setMemory(finalResJudgeCase.getMemory());
                    subTaskJudgeCaseVo.setScore(finalResJudgeCase.getScore());
                    subTaskJudgeCaseVo.setTime(finalResJudgeCase.getTime());
                    subTaskJudgeCaseVo.setStatus(finalResJudgeCase.getStatus());
                }
                subTaskJudgeCaseVo.setSubtaskDetailList(entry.getValue());
                subTaskJudgeCaseVOS.add(subTaskJudgeCaseVo);
            }
        }
        return subTaskJudgeCaseVOS;
    }

    public void updateSubmission(Judge judge) throws StatusFailException, StatusForbiddenException {
        if (judge == null || judge.getSubmitId() == null || judge.getShare() == null) {
            throw new StatusFailException("参数错误！");
        }

        LambdaQueryWrapper<Judge> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(Judge::getSubmitId, Judge::getCid, Judge::getUid)
                .eq(Judge::getSubmitId, judge.getSubmitId());

        Judge judgeInfo = judgeEntityService.getOne(queryWrapper);

        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        if (!userRole.getUid().equals(judgeInfo.getUid())) {
            throw new StatusForbiddenException("对不起，您不能修改他人的代码分享权限！");
        }
        if (judgeInfo.getCid() != 0) {
            throw new StatusForbiddenException("对不起，您不能修改比赛的代码分享权限！");
        }

        LambdaUpdateWrapper<Judge> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Judge::getShare, judge.getShare()).eq(Judge::getSubmitId, judge.getSubmitId());
        boolean updated = judgeEntityService.update(updateWrapper);
        if (!updated) {
            throw new StatusFailException("修改失败！");
        }
    }

    public String submitProblemTestJudge(TestJudgeDTO testJudgeDTO) throws AccessException, StatusFailException, StatusForbiddenException, StatusSystemErrorException {
        judgeValidator.validateTestJudgeInfo(testJudgeDTO);

        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        String lockKey = Constants.Account.TEST_JUDGE_LOCK.getCode() + userRole.getUid();
        long count = redisUtils.incr(lockKey, 1);

        if (count > 1) {
            Long expireTime = redisUtils.getExpire(lockKey);
            if (expireTime == null || expireTime == 0L) {
                redisUtils.expire(lockKey, 3);
            }
            throw new StatusForbiddenException("对不起，您使用在线调试过于频繁，请稍后再尝试！");
        }
        redisUtils.expire(lockKey, 3);

        Problem problem = problemEntityService.getById(testJudgeDTO.getPid());
        if (problem == null) {
            throw new StatusFailException("当前题目不存在！不支持在线调试！");
        }

        String uniqueKey = "TEST_JUDGE_" + IdUtil.simpleUUID();
        TestJudgeReq testJudgeReq = new TestJudgeReq();
        testJudgeReq.setMemoryLimit(problem.getMemoryLimit())
                .setTimeLimit(problem.getTimeLimit())
                .setStackLimit(problem.getStackLimit())
                .setCode(testJudgeDTO.getCode())
                .setLanguage(testJudgeDTO.getLanguage())
                .setUniqueKey(uniqueKey)
                .setExpectedOutput(testJudgeDTO.getExpectedOutput())
                .setTestCaseInput(testJudgeDTO.getUserInput())
                .setProblemJudgeMode(problem.getJudgeMode())
                .setIsRemoveEndBlank(problem.getIsRemoveEndBlank() || problem.getIsRemote())
                .setIsFileIO(problem.getIsFileIO())
                .setIoReadFileName(problem.getIoReadFileName())
                .setIoWriteFileName(problem.getIoWriteFileName());

        String userExtraFile = problem.getUserExtraFile();
        if (!StringUtils.isEmpty(userExtraFile)) {
            testJudgeReq.setExtraFile((HashMap<String, String>) JSONUtil.toBean(userExtraFile, Map.class));
        }

        judgeDispatcher.sendTestJudgeTask(testJudgeReq);
        redisUtils.set(uniqueKey, TestJudgeRes.builder().status(Constants.Judge.STATUS_PENDING.getStatus()).build(), 10 * 60);

        return uniqueKey;
    }


    public Judge submitProblemJudge(SubmitJudgeDTO judgeDto) throws AccessException, StatusFailException, StatusForbiddenException, StatusNotFoundException, StatusAccessDeniedException {
        judgeValidator.validateSubmissionInfo(judgeDto);

        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        boolean isContestSubmission = judgeDto.getCid() != null && judgeDto.getCid() != 0;
        boolean isTrainingSubmission = judgeDto.getTid() != null && judgeDto.getTid() != 0;

        SwitchConfig switchConfig = nacosSwitchConfig.getSwitchConfig();
        if (!isContestSubmission && switchConfig.getDefaultSubmitInterval() > 0) {
            String lockKey = Constants.Account.SUBMIT_NON_CONTEST_LOCK.getCode() + userRole.getUid();
            long count = redisUtils.incr(lockKey, 1);
            if (count > 1) {
                Long expire = redisUtils.getExpire(lockKey);
                if (expire == null || expire == 0L) {
                    redisUtils.expire(lockKey, 3);
                }
                throw new StatusForbiddenException("对不起，您的提交频率过快，请稍后再尝试！");
            }
            redisUtils.expire(lockKey, switchConfig.getDefaultSubmitInterval());
        }

        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        Judge judge = new Judge();
        judge.setShare(false) // 默认设置代码为单独自己可见
                .setCode(judgeDto.getCode())
                .setCid(judgeDto.getCid())
                .setGid(judgeDto.getGid())
                .setLanguage(judgeDto.getLanguage())
                .setLength(judgeDto.getCode().length())
                .setUid(userRole.getUid())
                .setUsername(userRole.getUsername())
                .setStatus(Constants.Judge.STATUS_PENDING.getStatus()) // 开始进入判题队列
                .setSubmitTime(new Date())
                .setVersion(0)
                .setIp(IpUtils.getUserIpAddr(request));

        if (isContestSubmission) {
            beforeDispatchInitManager.initContestSubmission(judgeDto.getCid(), judgeDto.getPid(), userRole, judge);
        } else if (isTrainingSubmission) {
            beforeDispatchInitManager.initTrainingSubmission(judgeDto.getTid(), judgeDto.getPid(), userRole, judge);
        } else {
            beforeDispatchInitManager.initCommonSubmission(judgeDto.getPid(), judgeDto.getGid(), judge);
        }

        if (judgeDto.getIsRemote()) {
            remoteJudgeDispatcher.sendTask(judge.getSubmitId(),
                    judge.getPid(),
                    judge.getDisplayPid(),
                    isContestSubmission,
                    false);
        } else {
            judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), isContestSubmission);
        }
        return judge;
    }

    public TestJudgeVO getTestJudgeResult(String testJudgeKey) throws StatusFailException {
        //测试结果保存：redisUtils.set(testJudgeReq.getUniqueKey(), testJudgeRes, 60);

        TestJudgeRes testJudgeRes = (TestJudgeRes) redisUtils.get(testJudgeKey);
        if (testJudgeRes == null) {
            throw new StatusFailException("查询错误！当前在线调试任务不存在！");
        }

        TestJudgeVO testJudgeVO = new TestJudgeVO();
        testJudgeVO.setStatus(testJudgeRes.getStatus());
        // 任为判题等待中
        if (Constants.Judge.STATUS_PENDING.getStatus().equals(testJudgeRes.getStatus())) {
            return testJudgeVO;
        }
        testJudgeVO.setUserInput(testJudgeRes.getInput());
        testJudgeVO.setUserOutput(testJudgeRes.getStdout());
        testJudgeVO.setExpectedOutput(testJudgeRes.getExpectedOutput());
        testJudgeVO.setMemory(testJudgeRes.getMemory());
        testJudgeVO.setTime(testJudgeRes.getTime());
        testJudgeVO.setStderr(testJudgeRes.getStderr()); //错误输出
        testJudgeVO.setProblemJudgeMode(testJudgeRes.getProblemJudgeMode());

        redisUtils.del(testJudgeKey);

        return testJudgeVO;
    }

    public HashMap<Long, Object> checkCommonJudgeResult(SubmitIdListDTO submitIdListDto) {

        List<Long> submitIds = submitIdListDto.getSubmitIds();
        if (CollectionUtils.isEmpty(submitIds)) {
            return new HashMap<>();
        }

        LambdaQueryWrapper<Judge> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(Judge.class, tableFieldInfo -> !tableFieldInfo.getColumn().equals("code")).in(Judge::getSubmitId, submitIds);
        List<Judge> judgeList = judgeEntityService.list(queryWrapper);

        HashMap<Long, Object> result = new HashMap<>();
        for (Judge judge : judgeList) {
            judge.setCode(null);
            judge.setErrorMessage(null);
            judge.setVjudgeUsername(null);
            judge.setVjudgeSubmitId(null);
            judge.setVjudgePassword(null);
            result.put(judge.getSubmitId(), judge);
        }
        return result;
    }

    public HashMap<Long, Object> checkContestJudgeResult(SubmitIdListDTO submitIdListDto) throws StatusNotFoundException {
        if (submitIdListDto.getCid() == null) {
            throw new StatusNotFoundException("非比赛类型");
        }

        if (CollectionUtils.isEmpty(submitIdListDto.getSubmitIds())) {
            return new HashMap<>();
        }

        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        boolean isRoot = SecurityUtils.getSubject().hasRole("root");

        Contest contest = contestEntityService.getById(submitIdListDto.getCid());

        boolean isContestAdmin = isRoot
                || userRole.getUid().equals(contest.getUid())
                || (contest.getIsGroup() && groupValidator.isGroupRoot(userRole.getUid(), contest.getGid()));

        boolean isSealRank = contestValidator.isSealRank(userRole.getUid(), contest, true, isRoot);

        LambdaQueryWrapper<Judge> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(Judge.class, info -> !info.getColumn().equals("code")).in(Judge::getSubmitId, submitIdListDto.getSubmitIds())
                .eq(Judge::getCid, submitIdListDto.getCid())
                .between(isSealRank, Judge::getSubmitTime, contest.getStartTime(), contest.getSealRankTime());

        List<Judge> judgeList = judgeEntityService.list(queryWrapper);
        HashMap<Long, Object> result = new HashMap<>();
        for (Judge judge : judgeList) {
            judge.setCode(null);
            judge.setDisplayPid(null);
            judge.setErrorMessage(null);
            judge.setVjudgeUsername(null);
            judge.setVjudgeSubmitId(null);
            judge.setVjudgePassword(null);
            if (!judge.getUid().equals(userRole.getUid()) && !isContestAdmin) {
                judge.setTime(null);
                judge.setMemory(null);
                judge.setLength(null);
            }
            result.put(judge.getSubmitId(), judge);
        }

        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    public Judge resubmit(Long submitId) throws StatusNotFoundException {
        Judge judge = judgeEntityService.getById(submitId);
        if (judge == null) {
            throw new StatusNotFoundException("数据不存在");
        }

        LambdaQueryWrapper<Problem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(Problem::getId, Problem::getIsRemote, Problem::getProblemId).eq(Problem::getId, judge.getPid());
        Problem problem = problemEntityService.getOne(queryWrapper);

        //非比赛
        if (judge.getCid() == 0) {
            // 重判前，需要将该题目对应记录表一并更新
            // 如果该题已经是AC通过状态，更新该题目的用户ac做题表 user_acproblem
            if (judge.getStatus().intValue() == Constants.Judge.STATUS_ACCEPTED.getStatus().intValue()) {
                LambdaQueryWrapper<UserAcproblem> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(UserAcproblem::getSubmitId, judge.getSubmitId());

                userAcproblemEntityService.remove(wrapper);
            }
        } else {
            if (problem.getIsRemote()) {
                LambdaUpdateWrapper<ContestRecord> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(ContestRecord::getSubmitId, submitId).setSql("status=null,score=null");
                contestRecordEntityService.update(updateWrapper);
            } else {
                throw new StatusNotFoundException("错误！非vJudge题目在比赛过程无权限重新提交"); //vJudge远程测评
            }
        }

        boolean isHasSubmitIdRemoteRejudge = Objects.nonNull(judge.getVjudgeSubmitId()) &&
                (judge.getStatus().intValue() == Constants.Judge.STATUS_SUBMITTED_FAILED.getStatus()
                        || judge.getStatus().intValue() == Constants.Judge.STATUS_PENDING.getStatus()
                        || judge.getStatus().intValue() == Constants.Judge.STATUS_JUDGING.getStatus()
                        || judge.getStatus().intValue() == Constants.Judge.STATUS_COMPILING.getStatus()
                        || judge.getStatus().intValue() == Constants.Judge.STATUS_SYSTEM_ERROR.getStatus());

        judge.setStatus(Constants.Judge.STATUS_PENDING.getStatus());
        judge.setVersion(judge.getVersion() + 1);
        judge.setErrorMessage(null)
                .setOiRankScore(null)
                .setScore(null)
                .setTime(null)
                .setJudger("")
                .setMemory(null);
        judgeEntityService.updateById(judge);


        if (problem.getIsRemote()) {
            remoteJudgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), problem.getProblemId(), judge.getCid() != 0, isHasSubmitIdRemoteRejudge);
        } else {
            judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judge.getCid() != 0);
        }
        return judge;
    }
}
